#!/usr/bin/python3
# -*- coding: UTF-8 -*-

# 定义函数的时候，我们把参数的名字和位置确定下来，函数的接口定义就完成了
# 必选参数、默认参数、可变参数和关键字参数

# 位置参数
# x的平方定义
def power1(x):
    return x * x
print(power1(3))

# x的n次方定义
def power2(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s
print(power2(3, 3))

# 默认参数 : 简化函数的调用\必选参数在前，默认参数在后
def power3(x, n = 2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s
print(power3(6))

# 默认参数使用
def enroll(name, gender, city = '上海', street = '新月路'):
    print("name", name)
    print("gender", gender)
    print("city", city)
    print("street", street)

enroll('张三', '男', city='北京')
enroll('李四', '女')

# 默认参数有个最大的坑:默认参数必须指向不变对象
def add_end(L=[]):
    L.append('END')
    return L
print(add_end())
print(add_end())
print(add_end())

def add_end1(L=None):
    if L is None:
        L = []
    L.append('END')
    return L
print(add_end1())
print(add_end1())
print(add_end1())

# str、None是的不变对象,不变对象一旦创建，对象内部的数据就不能修改，这样就减少了由于修改数据导致的错误

# 可变参数
# 一个list或tuple作为参数
def cale(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum
print(cale([1, 6, 8]))
print(cale((1, 6, 5)))

# 利用可变参数,简化调用
def cale(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum
print(cale(1, 2, 3))

# *nums表示把nums这个list的所有元素作为可变参数传进去
nums = [1, 3, 4]
print(cale(*nums))

# 关键字参数
# 关键字参数允许你传入0个或任意个含参数名的参数
# 这些关键字参数在函数内部自动组装为一个dict
def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

person('唐浩', 33)
person('唐浩', 33, city='长沙')
person('唐浩', 33, city='长沙', job = '作者')
# 用户注册的功能，除了用户名和年龄是必填项外，其他都是可选项，利用关键字参数来定义这个函数就能满足注册的需求。

extra = {'city' : '长沙', 'job' : '文学家'}
person('唐浩', 33, **extra)

# 命名关键字参数,限制关键字参数的名字
# 检查是否有city和job参数
def person(name, age, **kw):
    if 'city' in kw:
        print('存在city')
    if 'job' in kw:
        pass
    print(name, age, kw)
person('余耀华', 55, city='郑州')

def person(name, age, *, city, job):
    print(name, age, city, job)

person('余耀华', 55, city='郑州', job='作者')

# 必须传入参数名,没传会报错，不是定义的名字不起效
# 没有可变参数，就必须加一个*作为特殊分隔符


# 参数组合
# 参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数
def f1(a, b, c=0, *args, **kw):
    print('a=', a, 'b=', b, 'c=', c, 'args=', args, 'kw=', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a=', a, 'b=', b, 'c=', c, 'd=', d, 'kw=', kw)

f1(1,2)
f1(1,2,c=3)
f1(1,2, 3, 'a', 'b')
f1(1,2, 3, 'a', 'b', x=99)
f2(1,2, d=99, ext=None)

# 最神奇的是通过一个tuple和dict，你也可以调用上述函数
args = (1,2,3,4)
kw = {'d':99, 'x' : '#'}
f1(*args, **kw)
args = (1,2,3)
kw = {'d':88, 'x' : '#'}
f2(*args, **kw)


# *args是可变参数，args接收的是一个tuple
# **kw是关键字参数，kw接收的是一个dict